package com.wms.aspect;

import cn.hutool.log.Log;
import com.wms.annotation.MyLog;
import com.wms.entity.SysLog;
import com.wms.entity.SysUser;
import com.wms.mapper.SysLogMapper;
import com.wms.util.DateUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;

/**
 * ---Do First---
 *
 * @author:wellseasun
 * @date:2025/4/28 下午 1:13
 * @desc:日志切面,用于记录日志,解析日志注解,插入日志信息
 */
@Aspect
@Component
public class LogAspect {
    @Autowired
    private SysLogMapper logMapper;

    @After("@annotation(com.wms.annotation.MyLog)")
    public void recordLog(JoinPoint joinPoint) {
        asyncRecordLog(joinPoint);
    }

    @Async("logTaskExecutor") // 指定使用的线程池
    public void asyncRecordLog(JoinPoint joinPoint) {
        try {
            // 获得当前请求对象
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) return;

            HttpServletRequest request = attributes.getRequest();

            // ip
            String ip = getIpAddress(request);

            // method
            String method = request.getMethod();

            // 尝试从 session 中获取用户信息
            SysUser sysUser = null;
            try {
                sysUser = (SysUser) request.getSession(false).getAttribute("sysUser");
            } catch (Exception e) {
                // session 不可用时忽略
            }

            String name = sysUser != null ? sysUser.getUserName() : "未登录";

            // 解析日志注解
            HashMap<String, String> map = getAnnoLog(joinPoint);
            // 插入数据库
            SysLog sysLog = new SysLog( );
            sysLog.setLogTime(DateUtils.addFixedEightHours(new Date()));
            sysLog.setIp(ip);
            sysLog.setMethod(method);
            sysLog.setName(name);
            sysLog.setLog(map.get("log"));
            sysLog.setType(map.get("type"));
            logMapper.insertLog(sysLog);

        } catch (Exception e) {
            // 可选：记录日志失败时打印错误
            Log.get().error("记录操作日志失败", e);
        }
    }
    /**
     * 获得访问者ip地址
     */
    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    /**
     * 封装的方法,通过反射技术获得目标方法上的注解的值
     */
    public static HashMap<String, String> getAnnoLog(JoinPoint joinPoint) {
        HashMap<String, String> map = new HashMap<>( );
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature( );
            // 获得目标方法对象
            Method method = signature.getMethod( );
            // 获得目标方法对象上的日志注解
            MyLog myLog = method.getDeclaredAnnotation(MyLog.class);
            // 获得日志注解上的值
            String log = myLog.value( );
            String type = myLog.type( );

            map.put("log", log);
            map.put("type", type);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return map;
    }
}
